from Config.myConstant import *
from Config.myConfig import *
from DataAccess.TradedayDataProcess import TradedayDataProcess
from DataPrepare.tickFactorsProcess import tickFactorsProcess
from DataAccess.KLineDataProcess import KLineDataProcess
from DataPrepare.dailyFactorsProcess import dailyFactorsProcess
from Utility.JobLibUtility import JobLibUtility
from Strategy.baseStrategy.baseStrategy import baseStrategy
import pandas as pd
import numpy as np
import math
import xgboost as xgb
import warnings
from DataAccess.TickDataProcess import TickDataProcess
from DataPrepare.tickFactors.tickDataPrepared import tickDataPrepared
from DataAccess.IndexComponentDataProcess import IndexComponentDataProcess
from Utility.InfluxdbUtility import InfluxdbUtility
from joblib import Parallel, delayed,parallel_backend
########################################################################
class strategyAnalysis(object):
    """回测结果分析"""
    #----------------------------------------------------------------------
    def __init__(self,stockIndex,parameters):
        self.parameters=parameters
        self.stockIndex=stockIndex
        pass
    #----------------------------------------------------------------------
    def computeAverageWeight(self,codeList,startDate,endDate,stockIndex):
        days=list(TradedayDataProcess().getTradedays(startDate,endDate)).sort()
        myWeight=IndexComponentDataProcess.getIndexProperty(stockIndex,str(startDate),str(endDate))
        myWeight=myWeight[myWeight['code'].isin(codeList)]
        if myWeight.shape[0]>0:
            weight=pd.DataFrame()
            weight['weight']=myWeight.groupby('date')['weight'].sum()
            weight['date']=weight.index
        else:
            weight=pd.DataFrame(days,columns=['date'])
            weight['weight']=0
        return weight
        pass
    #----------------------------------------------------------------------
    def analysis(self,codeList,startDate,endDate,stockIndex,data):
        data=data[(data['date']>=str(startDate)) & (data['date']<=str(endDate))]
        days=list(TradedayDataProcess().getTradedays(startDate,endDate))
        days.sort()
        weightDF=self.computeAverageWeight(codeList,startDate,endDate,stockIndex)
        weight=weightDF['weight'].mean()
        if (weight==0):
            return [0,0,0,0]
        select=[]
        for code in codeList:
            myselect=data[data['code']==code]
            if (myselect.shape[0]>0):
                select.append(myselect)
        if (len(select)==0) :
            return [0,0,0,0]
        tradeall=pd.concat(select)
        parameter2List=list(tradeall['parameter2'].unique())
        parameter1List=list(tradeall['parameter1'].unique())
        performance=[]
        yieldArr=np.zeros([len(parameter2List),len(parameter1List)])
        turnoverArr=np.zeros([len(parameter2List),len(parameter1List)])
        winRateArr=np.zeros([len(parameter2List),len(parameter1List)])
        holdArr=np.zeros([len(parameter2List),len(parameter1List)])
        for i in range(len(parameter2List)):
            longOpen=parameter2List[i]
            for j in range(len(parameter1List)):
                shortOpen=parameter1List[j]
                trade=tradeall[((tradeall['parameter2']==longOpen) & (tradeall['parameter1']==shortOpen))]
                if trade.shape[0]>0:
                    #grade=trade['yieldToAll'].mean()
                    #grade=(trade['yieldToAll']*trade['weight']).sum()/(trade['weight'].sum())
                    yieldArr[i,j]=round(100*((trade['yieldToAll']*trade['weight']).sum()/weight)/(len(days))*250,3)
                    turnoverArr[i,j]=round(100*(trade['turnoverRate']*trade['weight']).sum()/weight/len(days),3)
                    winRateArr[i,j]=round(100*(trade['count']*trade['winRate']).sum()/trade['count'].sum(),3)
                    holdArr[i,j]=round(trade['totalHold'].sum()/trade['count'].sum(),3)
        yieldArr=pd.DataFrame(data=yieldArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        turnoverArr=pd.DataFrame(data=turnoverArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        winRateArr=pd.DataFrame(data=winRateArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        holdArr=pd.DataFrame(data=holdArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        return [yieldArr,turnoverArr,winRateArr,holdArr]
    #----------------------------------------------------------------------
    def evaluate2dParameters(self,codeList,startDate,endDate,stockIndex,dataAll):
        parameters=self.parameters
        mydata=dataAll.copy()
        data=mydata[(mydata['date']>=str(startDate)) & (mydata['date']<=str(endDate))]
        days=list(TradedayDataProcess().getTradedays(startDate,endDate))
        days.sort()
        weightDF=self.computeAverageWeight(codeList,startDate,endDate,stockIndex)
        weight=weightDF['weight'].mean()
        result=pd.DataFrame()
        if (weight==0):
            return result
        select=[]
        for code in codeList:
            myselect=data[data['code']==code]
            if (myselect.shape[0]>0):
                select.append(myselect)
        if (len(select)==0) :
            return result
        tradeall=pd.concat(select)
        col1=parameters['colName'][0]
        col2=parameters['colName'][1]
        paraName1=parameters['paraName'][0]
        paraName2=parameters['paraName'][1]
        parameter1List=list(tradeall[col1].unique())
        parameter2List=list(tradeall[col2].unique())
        result=[]
        for i in range(len(parameter1List)):
            for j in range(len(parameter2List)):
                if (abs(i-j)>=3):
                    continue
                trade=tradeall[((tradeall[col1]==parameter1List[i]) & (tradeall[col2]==parameter2List[j]))]
                if trade.shape[0]>0:
                    #grade=trade['yieldToAll'].mean()
                    #grade=(trade['yieldToAll']*trade['weight']).sum()/(trade['weight'].sum())
                    yield0=round(100*((trade['yieldToAll']*trade['weight']).sum()/weight)/(len(days))*250,3)
                    turnover0=round(100*(trade['turnoverRate']*trade['weight']).sum()/weight/len(days),3)
                    winRate0=round(100*(trade['count']*trade['winRate']).sum()/trade['count'].sum(),3)
                    hold0=round(trade['totalHold'].sum()/trade['count'].sum(),3)
                    count0=round(trade['count'].sum()/len(days),3)
                    result.append({'para1':paraName1,'para2':paraName2,'yield':yield0,'turnover':turnover0,'winRate':winRate0,'hold':hold0,'para1Value':parameter1List[i],'para2Value':parameter2List[j],'count':count0})
        
        result=pd.DataFrame(result)
        result['weight']=weight
        return result
    #----------------------------------------------------------------------
    def useJobLibToComputeByCodes(self,myfunction,stockCodes,groupnum,days,startDate,endDate,period,data):
        warnings.filterwarnings('ignore')
        if groupnum>len(stockCodes):
            groupnum=len(stockCodes)
            pass
        stocks={i:[] for i in range(groupnum)}
        for i in range(0,len(stockCodes)):
            mygroup=i%groupnum
            stocks[mygroup].append(stockCodes[i])
        tmpAddress={}
        with parallel_backend("multiprocessing", n_jobs=MYJOBS):
            mydata=Parallel()(delayed(myfunction)(list(stocks[i]),days,startDate,endDate,period,data) for i in range(groupnum))
        mydata=pd.concat(mydata)
        return mydata
    #----------------------------------------------------------------------
    def multipleCodes_parallel(self,codes,days,startDate,endDate,period,data):
        mydata=self.useJobLibToComputeByCodes(self.computeYield,codes,MYGROUPS,days,startDate,endDate,period,data)
        return mydata
    #----------------------------------------------------------------------
    def computeYield(self,codes,days,startDate,endDate,period,data):
        answer=[]
        parameters=self.parameters
        col1=parameters['colName'][0]
        col2=parameters['colName'][1]
        paraName1=parameters['paraName'][0]
        paraName2=parameters['paraName'][1]
        parameter1List=list(data[col1].unique())
        parameter2List=list(data[col2].unique())
        para1Value=parameter1List[0]
        para2Value=parameter2List[0]
        for code in codes:
            myyield=[]
            codeWeight=0
            for dateIndex in range(len(days)-period):
                date1=days[dateIndex]
                date2=days[dateIndex+period-1]
                date3=days[dateIndex+period]
                evaluate=self.evaluate2dParameters([code],date1,date2,self.stockIndex,data)
                if (evaluate.shape[0]>0):
                    para1Value=evaluate.iloc[evaluate['yield'].idxmax()]['para1Value']
                    para2Value=evaluate.iloc[evaluate['yield'].idxmax()]['para2Value']
                evaluate0=self.evaluate2dParameters([code],date3,date3,self.stockIndex,data)
                if (evaluate0.shape[0]>0):
                    now=evaluate0[(evaluate0['para1Value']==para1Value) & (evaluate0['para2Value']==para2Value)]
                    if now.shape[0]>0:
                        myyield.append(now['yield'].iloc[0])
                        codeWeight=now['weight'].iloc[0]
                    else:
                        myyield.append(0.0)
                    pass
                else:
                    myyield.append(0.0)
            yieldMean=np.mean(myyield)
            answer.append({'code':code,'yield':yieldMean,'weight':codeWeight,'startDate':days[period],'endDate':days[-1]})
            #print(answer[-1])
        answer=pd.DataFrame(answer)
        #print(answer)
        return answer
########################################################################

